🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / core / ble / src / commonMain / kotlin / org / meshtastic / core / ble / KableMeshtasticRadioProfile.kt
Displaying Raw • Download
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableMeshtasticRadioProfile.kt e23fab266758cd1fcacaf55e5cf25aea4c5cf00e (e23fab26) Text, 6.14 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.ble
Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787kotlinx.coroutines.CancellationException
Tff7b72import T7ee787kotlinx.coroutines.channels.BufferOverflow
Tff7b72import T7ee787kotlinx.coroutines.delay
Tff7b72import T7ee787kotlinx.coroutines.flow.Flow
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableSharedFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.channelFlow
Tff7b72import T7ee787kotlinx.coroutines.launch
Tff7b72import T7ee787org.meshtastic.core.ble.MeshtasticBleConstants.FROMNUM_CHARACTERISTIC
Tff7b72import T7ee787org.meshtastic.core.ble.MeshtasticBleConstants.FROMRADIOSYNC_CHARACTERISTIC
Tff7b72import T7ee787org.meshtastic.core.ble.MeshtasticBleConstants.FROMRADIO_CHARACTERISTIC
Tff7b72import T7ee787org.meshtastic.core.ble.MeshtasticBleConstants.LOGRADIO_CHARACTERISTIC
Tff7b72import T7ee787org.meshtastic.core.ble.MeshtasticBleConstants.TORADIO_CHARACTERISTIC
T8b949e/**
* [MeshtasticRadioProfile] implementation using Kable BLE characteristics.
*
* Supports both the modern `FROMRADIOSYNC` characteristic (single observe stream) and the legacy `FROMNUM` +
* `FROMRADIO` polling fallback for older firmware versions.
*/
Tff7b72class T56d364KableMeshtasticRadioProfileTb4b4b4(Tff7b72private Tff7b72val Te6edf3serviceTb4b4b4: Te6edf3BleServiceTb4b4b4) Tb4b4b4: Te6edf3MeshtasticRadioProfile Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3toRadio Tff7b72= Te6edf3serviceTb4b4b4.Te6edf3characteristicTb4b4b4(Te6edf3TORADIO_CHARACTERISTICTb4b4b4)
Tff7b72private Tff7b72val Te6edf3fromRadioChar Tff7b72= Te6edf3serviceTb4b4b4.Te6edf3characteristicTb4b4b4(Te6edf3FROMRADIO_CHARACTERISTICTb4b4b4)
Tff7b72private Tff7b72val Te6edf3fromRadioSync Tff7b72= Te6edf3serviceTb4b4b4.Te6edf3characteristicTb4b4b4(Te6edf3FROMRADIOSYNC_CHARACTERISTICTb4b4b4)
Tff7b72private Tff7b72val Te6edf3fromNum Tff7b72= Te6edf3serviceTb4b4b4.Te6edf3characteristicTb4b4b4(Te6edf3FROMNUM_CHARACTERISTICTb4b4b4)
Tff7b72private Tff7b72val Te6edf3logRadioChar Tff7b72= Te6edf3serviceTb4b4b4.Te6edf3characteristicTb4b4b4(Te6edf3LOGRADIO_CHARACTERISTICTb4b4b4)
Tff7b72companion Tff7b72object Tb4b4b4{
Tff7b72private Tff7b72const Tff7b72val Te6edf3TRANSIENT_RETRY_DELAY_MS Tff7b72= T79c0ff5T79c0ff0T79c0ff0L
Tb4b4b4}
T8b949e// replay = 1: a seed emission placed here before the collector starts is replayed to the
T8b949e// collector immediately on subscription. This is what drives the initial FROMRADIO poll
T8b949e// during the config-handshake phase, where the firmware suppresses FROMNUM notifications
T8b949e// (it only emits them in STATE_SEND_PACKETS). Without the initial replay the entire config
T8b949e// stream would be silently skipped on devices that lack FROMRADIOSYNC.
Tff7b72private Tff7b72val Te6edf3triggerDrain Tff7b72=
Te6edf3MutableSharedFlowTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Te6edf3replay Tff7b72= T79c0ff1Tb4b4b4, Te6edf3extraBufferCapacity Tff7b72= T79c0ff6T79c0ff4Tb4b4b4, Te6edf3onBufferOverflow Tff7b72= Te6edf3BufferOverflowTb4b4b4.Te6edf3DROP_OLDESTTb4b4b4)
T8b949e// Using observe() for fromRadioSync or legacy read loop for fromRadio
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffSwallowedExceptionTa5d6ff"Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3fromRadioTb4b4b4: Te6edf3FlowTff7b72<Te6edf3ByteArrayTff7b72> Tff7b72= Te6edf3channelFlow Tb4b4b4{
T8b949e// Try to observe FROMRADIOSYNC if available. If it fails, fallback to FROMNUM/FROMRADIO.
T8b949e// This mirrors the robust fallback logic originally established in the legacy Android Nordic implementation.
Te6edf3launch Tb4b4b4{
Tff7b72try Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3serviceTb4b4b4.Te6edf3hasCharacteristicTb4b4b4(Te6edf3fromRadioSyncTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3serviceTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3fromRadioSyncTb4b4b4)Tb4b4b4.Te6edf3collect Tb4b4b4{ Te6edf3sendTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3errorTb4b4b4(Ta5d6ff"Ta5d6fffromRadioSync missingTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3CancellationExceptionTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3e
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3_Tb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
T8b949e// Fallback to legacy FROMNUM/FROMRADIO polling.
T8b949e// Wire up FROMNUM notifications for steady-state packet delivery.
Te6edf3launch Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3serviceTb4b4b4.Te6edf3hasCharacteristicTb4b4b4(Te6edf3fromNumTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3serviceTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3fromNumTb4b4b4)Tb4b4b4.Te6edf3collect Tb4b4b4{ Te6edf3triggerDrainTb4b4b4.Te6edf3tryEmitTb4b4b4(Tffa657UnitTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
T8b949e// Seed the replay buffer so the collector below starts draining immediately.
T8b949e// The firmware does NOT send FROMNUM notifications during the config handshake
T8b949e// (it gates them on STATE_SEND_PACKETS). Without this seed the entire config
T8b949e// stream would never be read on devices that lack FROMRADIOSYNC.
Te6edf3triggerDrainTb4b4b4.Te6edf3tryEmitTb4b4b4(Tffa657UnitTb4b4b4)
Te6edf3triggerDrainTb4b4b4.Te6edf3collect Tb4b4b4{
Tff7b72var Te6edf3keepReading Tff7b72= Tff7b72true
Tff7b72while Tb4b4b4(Te6edf3keepReadingTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3serviceTb4b4b4.Te6edf3hasCharacteristicTb4b4b4(Te6edf3fromRadioCharTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3keepReading Tff7b72= Tff7b72false
Tff7b72continue
Tb4b4b4}
Tff7b72val Te6edf3packet Tff7b72= Te6edf3serviceTb4b4b4.Te6edf3readTb4b4b4(Te6edf3fromRadioCharTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3packetTb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4) Te6edf3keepReading Tff7b72= Tff7b72false Tff7b72else Te6edf3sendTb4b4b4(Te6edf3packetTb4b4b4)
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3CancellationExceptionTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3e
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3wTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffFROMRADIO read error, pausing before next drain triggerTa5d6ff" Tb4b4b4}
Te6edf3keepReading Tff7b72= Tff7b72false
T8b949e// Don't permanently stop — the next triggerDrain emission will retry.
Te6edf3delayTb4b4b4(Te6edf3TRANSIENT_RETRY_DELAY_MSTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffSwallowedExceptionTa5d6ff"Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3logRadioTb4b4b4: Te6edf3FlowTff7b72<Te6edf3ByteArrayTff7b72> Tff7b72= Te6edf3channelFlow Tb4b4b4{
Tff7b72try Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3serviceTb4b4b4.Te6edf3hasCharacteristicTb4b4b4(Te6edf3logRadioCharTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3serviceTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3logRadioCharTb4b4b4)Tb4b4b4.Te6edf3collect Tb4b4b4{ Te6edf3sendTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3CancellationExceptionTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3e
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3_Tb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
T8b949e// logRadio is optional, ignore if not found
Tb4b4b4}
Tb4b4b4}
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffsendToRadioTb4b4b4(Te6edf3packetTb4b4b4: Te6edf3ByteArrayTb4b4b4) Tb4b4b4{
Te6edf3serviceTb4b4b4.Te6edf3writeTb4b4b4(Te6edf3toRadioTb4b4b4, Te6edf3packetTb4b4b4, Te6edf3serviceTb4b4b4.Te6edf3preferredWriteTypeTb4b4b4(Te6edf3toRadioTb4b4b4)Tb4b4b4)
Te6edf3triggerDrainTb4b4b4.Te6edf3tryEmitTb4b4b4(Tffa657UnitTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.4 - Generated in 0.12s